import { Asset, AssetManager, assetManager, ImageAsset, isValid, JsonAsset, SceneAsset, SpriteFrame, Texture2D } from 'cc';
import { Singleton } from './BaseManager';
import { LogUtil } from '../../Util/LogUtil';
import { CallBackHandler } from '../Model/PoolObj/CallBackHandler';
import { LoadingBundle } from '../Config/BaseEnum';
/* eslint-disable */
export class LoadingManager extends Singleton {
  className: string = 'LoadingManager';
  /** 动态资源，被频繁使用的资源 */
  private _dynamicsRes: Map<string, Asset> = new Map<string, Asset>();
  private _scenes: Map<string, SceneAsset> = new Map<string, SceneAsset>();
  private _resRemote: Map<string, SpriteFrame> = new Map<string, SpriteFrame>();

  loadBundle(bundleName: string | string[], onProgress: ((rate: number) => void) | null, onComplete: CallBackHandler | null);
  loadBundle(bundleName: string | string[], onProgress: ((rate: number) => void) | null);
  loadBundle(bundleName: string | string[], onComplete: CallBackHandler | null);
  loadBundle(bundleName: string | string[]);
  loadBundle(bundleName: string | string[], onProgress?: ((rate: number) => void) | CallBackHandler | null, onComplete?: CallBackHandler | null) {
    let bundleNameArr: string[];
    if (Array.isArray(bundleName)) {
      bundleNameArr = bundleName;
    } else {
      bundleNameArr = [bundleName];
    }
    let onComp = null;
    let onProg = null;
    if (onProgress instanceof CallBackHandler) {
      onComp = onProgress;
      onProg = null;
    } else {
      onComp = onComplete;
      onProg = onProgress;
    }
    const len = bundleNameArr.length;
    let i = 0;
    bundleNameArr.forEach((bundleName) => {
      assetManager.loadBundle(bundleName, (err) => {
        if (err) {
          throw new Error('LoadingManager=>loadBundle(): ' + bundleNameArr[i] + '资源包加载失败');
        }
        i++;
        onProg && onProg(i / len);
        if (i === len) onComp?.execute();
        // LogUtil.i('LoadingManager', 'loadBundle(): ', bundleName, ' OK!');
      });
    });
  }

  /** 获取资源包 */
  getBundle(bundleName: string): AssetManager.Bundle {
    const bundles = assetManager.bundles;
    if (!bundles.has(bundleName)) throw new Error('LoadingManager=>getBundle(): ' + bundleName + '资源包没有加载');
    return bundles.get(bundleName);
  }

  loadScene(path, onProgress: ((rate: number) => void) | null, onComplete: CallBackHandler): void;
  loadScene(path, onProgress: ((rate: number) => void) | CallBackHandler | null): void;
  loadScene(path, onComplete: CallBackHandler): void;
  loadScene(path): void;
  loadScene(path, onProgress?: ((rate: number) => void) | CallBackHandler | null, onComplete?: CallBackHandler): void {
    let onComp = null;
    let onProg = null;
    if (onProgress instanceof CallBackHandler) {
      onComp = onProgress;
      onProg = null;
    } else {
      onComp = onComplete;
      onProg = onProgress;
    }
    if (this._scenes.has(path)) {
      onProg && onProg(1);
      onComplete?.execute();
      return;
    }
    this.getBundle(LoadingBundle.SceneBundle).loadScene(
      path,
      (finished: number, total: number) => {
        let rate = finished / total;
        if (rate >= 1) rate = 0.999;
        onProg && onProg(rate);
      },
      (err, scene) => {
        if (err) {
          throw new Error('LoadingManager=>loadScene(): ' + path + '场景预加载失败');
        }
        this._scenes.set(path, scene);
        scene.addRef();
        onComp?.execute(scene);
        onProg && onProg(1);
      }
    );
  }

  async getLoadScene(path: string): Promise<SceneAsset> {
    if (this._scenes.has(path)) {
      return this._scenes.get(path);
    }
    const bundles = assetManager.bundles;
    const self = this;
    return new Promise((resolve, reject) => {
      bundles.get(LoadingBundle.SceneBundle).load<SceneAsset>(path, (err, scene) => {
        if (err) throw new Error('LoadingManager=>getLoadScene(): ' + path + '加载资源出错');
        self._scenes.set(path, scene);
        resolve(scene);
      });
    });
  }

  private _loadResComp: Map<string, Array<CallBackHandler>> = new Map<string, Array<CallBackHandler>>();

  /** 直接从这里加载的资源将不会被管理 */
  loadResource<T extends Asset>(path: string | string[], onProgress: ((rate: number) => void) | null, onComplete: CallBackHandler);
  loadResource<T extends Asset>(path: string | string[], onProgress: ((rate: number) => void) | null);
  loadResource<T extends Asset>(path: string | string[], onComplete: CallBackHandler | null);
  loadResource<T extends Asset>(path: string | string[]);
  loadResource<T extends Asset>(path: string | string[], onProgress?: ((rate: number) => void) | CallBackHandler | null, onComplete?: CallBackHandler | null) {
    let pathArr: string[];
    if (Array.isArray(path)) {
      pathArr = path;
    } else {
      pathArr = [path];
    }
    let onComp = null;
    let onProg = null;
    if (onProgress instanceof CallBackHandler) {
      onComp = onProgress;
      onProg = null;
    } else {
      onComp = onComplete;
      onProg = onProgress;
    }
    const len = pathArr.length;
    const ress = [];
    let i = 0;

    pathArr.forEach((path) => {
      if (this._dynamicsRes.has(path)) {
        i++;
        ress.push(this._dynamicsRes.get(path));
        let rate = i / len;
        if (rate >= 1) rate = 0.999;
        onProg && onProg(i / len);
        if (ress.length === len) {
          onProg && onProg(1);
          onComp?.execute(ress);
        }
        return;
      }

      const index = path.indexOf('/');
      const bundleName = path.substring(0, index);
      const resPath = path.substring(index + 1);
      const bundles = assetManager.bundles;
      if (!bundles.has(bundleName)) {
        throw new Error('LoadingManager=>loadResource(): ' + bundleName + '资源包没有加载');
      }

      if (!this._loadResComp.has(path)) this._loadResComp.set(path, []);
      onComp && this._loadResComp.get(path).push(onComp);
      if (this._loadResComp.get(path).length > 1) return;
      // bundles.get(bundleName).preload(resPath, (err, data) => {
      //   console.log(resPath, 'preload finish');
      bundles.get(bundleName).load<T>(
        resPath,
        (finished: number, total: number, item: any) => {
          let rate = (finished / total + i) / len;
          if (rate >= 1) rate = 0.999;
          onProg && onProg(rate);
          if (finished === total) i++;
        },
        (err, data) => {
          if (err) throw new Error('LoadingManager=>loadResource(): ' + resPath + '加载资源出错');
          this._dynamicsRes.set(path, data);
          ress.push(data);
          // console.log(resPath, 'load finish', data);
          if (ress.length === len) {
            // console.log(resPath, 'load finish', ress);
            onProg && onProg(1);
            while (this._loadResComp.get(path).length > 0) {
              const cb = this._loadResComp.get(path).shift();
              cb?.execute(ress);
            }
            this._loadResComp.delete(path);
            // onComp?.execute(ress);
          }
        }
      );
      // });
    });
  }

  /** 直接从这里加载的资源将不会被管理 */
  loadResByBundle<T extends Asset>(bundle: LoadingBundle, path: string[], onProgress: ((rate: number) => void) | null, onComplete: CallBackHandler);
  loadResByBundle<T extends Asset>(bundle: LoadingBundle, path: string[], onProgress: ((rate: number) => void) | null);
  loadResByBundle<T extends Asset>(bundle: LoadingBundle, path: string[], onComplete: CallBackHandler | null);
  loadResByBundle<T extends Asset>(bundle: LoadingBundle, path: string[]);
  loadResByBundle<T extends Asset>(bundle: LoadingBundle, path: string[], onProgress?: ((rate: number) => void) | CallBackHandler | null, onComplete?: CallBackHandler | null) {
    let onComp = null;
    let onProg = null;
    if (onProgress instanceof CallBackHandler) {
      onComp = onProgress;
      onProg = null;
    } else {
      onComp = onComplete;
      onProg = onProgress;
    }
    const bundles = assetManager.bundles;
    if (!bundles.has(bundle)) {
      throw new Error('LoadingManager=>loadResource(): ' + bundle + '资源包没有加载');
    }
    bundles.get(bundle).load<T>(
      path,
      (finished: number, total: number, item: any) => {
        let rate = finished / total;
        if (rate >= 1) rate = 0.999;
        onProg && onProg(rate);
      },
      (err, data) => {
        if (err) throw new Error('LoadingManager=>loadResource(): ' + path[0] + '加载资源出错');
        for (let i = 0; i < data.length; i++) {
          this._dynamicsRes.set(bundle + '/' + path[i], data[i]);
        }
        onProg && onProg(1);
        onComp?.execute(data);
      }
    );
  }

  async getLoadRes<T extends Asset>(path: string): Promise<T> {
    if (this._dynamicsRes.has(path)) {
      return this._dynamicsRes.get(path) as T;
    }
    const index = path.indexOf('/');
    const bundleName = path.substring(0, index);
    const resPath = path.substring(index + 1);
    const bundles = assetManager.bundles;
    if (!bundles.has(bundleName)) {
      throw new Error('LoadingManager=>loadAsset(): ' + bundleName + '资源包没有加载');
    }
    const self = this;
    return new Promise((resolve, reject) => {
      bundles.get(bundleName).load<T>(resPath, (err, data) => {
        if (err) throw new Error('LoadingManager=>loadAsset(): ' + resPath + '加载资源出错');
        self._dynamicsRes.set(path, data);
        resolve(data);
      });
    });
  }

  /** 获取已经加载的资源 */
  getResource<T extends Asset>(path: string): T {
    const res = this._dynamicsRes.get(path);
    if (res == null) {
      throw new Error('LoadingManager=>getResource(): ' + path + '资源没有加载');
    }
    return res as T;
  }

  /** 动态加载资源，并增加引用计数 */
  async loadDynamicsResByBundle<T extends Asset>(bundle: LoadingBundle, path: string[]): Promise<T[]> {
    return new Promise((resolve, reject) => {
      this.loadResByBundle<T>(
        bundle,
        path,
        CallBackHandler.pop((data) => {
          for (let i = 0; i < data.length; i++) {
            data[i].addRef();
          }
          resolve(data);
        }, this)
      );
    });
  }

  /** 动态加载资源，并增加引用计数 */
  async loadDynamicsRes<T extends Asset>(path: string): Promise<T>;
  async loadDynamicsRes<T extends Asset>(path: string[]): Promise<T[]>;
  async loadDynamicsRes<T extends Asset>(path: string | string[]): Promise<T | T[]> {
    if (Array.isArray(path)) {
      return new Promise((resolve, reject) => {
        this.loadResource<T>(
          path,
          CallBackHandler.pop((data: T[]) => {
            for (let i = 0; i < data.length; i++) {
              data[i].addRef();
            }
            return resolve(data);
          })
        );
      });
    } else {
      return new Promise((resolve, reject) => {
        this.loadResource<T>(
          path,
          CallBackHandler.pop((data: T[]) => {
            data[0].addRef();
            return resolve(data[0]);
          })
        );
      });
    }
  }

  /** 释放资源引用 */
  releaseDynamicRes(key: string | string[]): void {
    let keys;
    if (Array.isArray(key)) {
      keys = key;
    } else {
      keys = [key];
    }
    keys.forEach((key) => {
      const res = this._dynamicsRes.get(key);
      if (res) {
        res.decRef();
        if (res.refCount <= 0) {
          this._dynamicsRes.delete(key);
        }
      } else {
        LogUtil.e('LoadingManager=>releaseDynamicRes(): ', key, 'ERROR! 资源被提前释放？');
      }
    });
  }

  /** !!!!谨慎使用，尝试释放加载过的所有资源 */
  removeResAll() {
    this._dynamicsRes.forEach((res) => {
      assetManager.releaseAsset(res);
    });
    this._dynamicsRes.clear();
  }

  /** 加载远程资源 */
  async loadResRemote(
    url: string,
    options?: {
      [k: string]: any;
      ext?: string;
    }
  ): Promise<SpriteFrame> {
    const self = this;
    return new Promise<SpriteFrame>((resolve, reject) => {
      assetManager.loadRemote<ImageAsset>(url, options, function (err, data) {
        if (err) {
          reject(err);
        } else {
          let spriteFrame: SpriteFrame;
          if (!self._resRemote.has(data.uuid)) {
            spriteFrame = SpriteFrame.createWithImage(data);
            spriteFrame.texture._uuid = 'texture.' + data.uuid;
            self._resRemote.set(data.uuid, spriteFrame);
            data.addRef();
          } else {
            spriteFrame = self._resRemote.get(data.uuid) as SpriteFrame;
          }
          spriteFrame.addRef();
          resolve(spriteFrame);
        }
      });
    });
  }

  private _resRemote2: Map<string, JsonAsset> = new Map();
  /** 加载远程资源 */
  async loadResRemote2(url: string): Promise<JsonAsset> {
    const self = this;
    return new Promise<JsonAsset>((resolve, reject) => {
      assetManager.loadRemote<JsonAsset>(url, function (err, data) {
        if (err) {
          reject(err);
        } else {
          let json: JsonAsset;
          if (!self._resRemote2.has(data.uuid)) {
            json = data;
            self._resRemote2.set(data.uuid, json);
            data.addRef();
          } else {
            json = self._resRemote2.get(data.uuid) as JsonAsset;
          }
          json.addRef();
          resolve(json);
        }
      });
    });
  }

  releaseRemoteSprite(sp: SpriteFrame) {
    if (!isValid(sp)) {
      return;
    }
    sp.decRef(false); // 只把计数减1
    if (sp.refCount <= 0) {
      let texture = sp.texture as Texture2D;
      // 如果已加入动态合图，必须取原始的Texture2D
      if (sp.packable) {
        texture = sp.original?._texture as Texture2D;
      }
      if (texture) {
        this._resRemote.delete(texture.image!.uuid); // 删除远程资源记录
        texture.image?.decRef();
        texture.destroy();
      }
      sp.destroy();
    }
  }
}
